package cn.parser;

import cn.relation.RelationEntity;
import cn.structure.AbstractInvocationEntity;
import cn.structure.BaseEntity;
import cn.structure.ClassEntity;
import cn.structure.ConstructorCallEntity;
import cn.structure.ExecutableEntity;
import cn.structure.InvocationEntity;
import cn.structure.MethodEntity;
import cn.structure.VariableEntity;
import cn.structure.edp.AbstractInterface;
import cn.structure.edp.Conglomeration;
import cn.structure.edp.CreateObject;
import cn.structure.edp.DelegatedConglomeration;
import cn.structure.edp.Delegation;
import cn.structure.edp.DeputizedDelegation;
import cn.structure.edp.DeputizedRedirection;
import cn.structure.edp.EdpEntity;
import cn.structure.edp.ExtendMethod;
import cn.structure.edp.Inheritance;
import cn.structure.edp.Recursion;
import cn.structure.edp.RedirectedRecursion;
import cn.structure.edp.Redirection;
import cn.structure.edp.Retrieve;
import cn.structure.edp.RevertMethod;
import cn.structure.edp.TrustRedirection;
import cn.structure.edp.TrustedDelegation;
import cn.type.CallerTypeEnum;
import cn.type.EntityTypeEnum;
import cn.type.RelTypeEnum;
import cn.type.VariableScopeEnum;
import cn.utils.SpoonUtil;
import spoon.reflect.code.CtAssignment;
import spoon.reflect.code.CtFieldAccess;
import spoon.reflect.code.CtFieldRead;
import spoon.reflect.code.CtFieldWrite;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.TypeFilter;

import java.util.HashSet;
import java.util.Set;

public class EdpParser {
    private static EdpParser edpParser = new EdpParser();
    private static EntityFactory factory = EntityFactoryImpl.getInstance();
    private static Set<EdpEntity> edp_set = new HashSet<>();

    public static EdpParser getInstance() {
        return edpParser;
    }

    public void createEdpRelation(BaseEntity edp, BaseEntity target, Enum<?> role) {
        RelationEntity relationEntity = factory.createRelationEntity(edp, target, RelTypeEnum.HAS_ROLE, EntityTypeEnum.EdpEntity);
        factory.addRelationProperty(relationEntity, "role", role.toString());
    }

    public Set<EdpEntity> getEdpEntities() {
        return edp_set;
    }

    public void parseCreateObject() {
        for (RelationEntity relation : EntityFactory.relationship_set) {
            BaseEntity source = relation.getSource();
            BaseEntity target = relation.getTarget();
            RelTypeEnum relType = relation.getRelation_type();
            if (source instanceof ConstructorCallEntity) {
                ConstructorCallEntity objectCreated = (ConstructorCallEntity) source;
                BaseEntity creationSite = SpoonUtil.getSingleIncoming(source, RelTypeEnum.HAS_INVOKE);
                if (relType.equals(RelTypeEnum.CALLED)) {
                    ClassEntity typeToCreate = (ClassEntity) SpoonUtil.getSingleIncoming(target, RelTypeEnum.HAS_CONSTRUCTOR);
                    if (creationSite != null && typeToCreate != null) {
                        CreateObject createObject = new CreateObject(creationSite, typeToCreate, objectCreated);
                        edp_set.add(createObject);
                        createEdpRelation(createObject, creationSite, CreateObject.Role.creationSite);
                        createEdpRelation(createObject, typeToCreate, CreateObject.Role.typeToCreate);
                        createEdpRelation(createObject, objectCreated, CreateObject.Role.objectCreated);
                    }
                }
            }
        }
    }

    public void parseRetrieve() {
        for (ClassEntity sinkHolder : EntityFactory.classEntity_set) {
            for (CtMethod<?> method : sinkHolder.getMethods()) {
                for (CtAssignment assignment : method.getElements(new TypeFilter<>(CtAssignment.class))) {
                    Set<String> references = new HashSet<>(3);
                    references.add(sinkHolder.getName());
                    VariableEntity sink = null;
                    ClassEntity sourceAccess = null;
                    ClassEntity retrieved = null;
                    VariableEntity source = null;
                    for (CtFieldAccess<?> fieldAccess : assignment.getElements(new TypeFilter<>(CtFieldAccess.class))) {
                        if (fieldAccess.getVariable() != null) {
                            if (fieldAccess instanceof CtFieldWrite && fieldAccess.getVariable().getFieldDeclaration() != null) {
                                sink = factory.createVariableEntity(fieldAccess.getVariable().getFieldDeclaration(), VariableScopeEnum.FIELD);
                                retrieved = factory.getClassEntityByQualifiedName(fieldAccess.getType().getQualifiedName());
                                references.add(fieldAccess.getType().getQualifiedName());
                                if (assignment.getAssignment() instanceof CtInvocation) {
                                    CtInvocation<?> ctInvocation = (CtInvocation<?>) assignment.getAssignment();
                                    if (ctInvocation.getTarget() != null) {
                                        references.add(ctInvocation.getTarget().getType().getQualifiedName());
                                        sourceAccess = factory.getClassEntityByQualifiedName(ctInvocation.getTarget().getType().getQualifiedName());
                                        for (CtField<?> ctField : sinkHolder.getFields()) {
                                            if (ctField.getSimpleName().equals(ctInvocation.getTarget().toString())) {
                                                source = factory.createVariableEntity(ctField, VariableScopeEnum.FIELD);
                                            }
                                        }
                                    }
                                }
                            }
                            if (fieldAccess instanceof CtFieldRead) {
                                if (fieldAccess.getTarget() instanceof CtFieldRead) {
                                    references.add(fieldAccess.getTarget().getType().getQualifiedName());
                                    sourceAccess = factory.getClassEntityByQualifiedName(fieldAccess.getTarget().getType().getQualifiedName());
                                    source = factory.createVariableEntity(fieldAccess.getVariable().getFieldDeclaration(), VariableScopeEnum.FIELD);
                                }
                            }
                        }
                    }
                    if (references.size() > 2 && sink != null && sourceAccess != null && retrieved != null && source != null) {
                        Retrieve retrieve = new Retrieve(sourceAccess, sinkHolder, retrieved, sink, source);
                        edp_set.add(retrieve);
                        createEdpRelation(retrieve, sourceAccess, Retrieve.role.sourceAccess);
                        createEdpRelation(retrieve, sinkHolder, Retrieve.role.sinkHolder);
                        createEdpRelation(retrieve, retrieved, Retrieve.role.retrieved);
                        createEdpRelation(retrieve, sink, Retrieve.role.sink);
                        createEdpRelation(retrieve, source, Retrieve.role.source);
                    }
                }
            }
        }
    }

    public void parseInheritance() {
        Set<ClassEntity> cls_set = new HashSet<>(EntityFactory.classEntity_set);
        for (ClassEntity subClass : cls_set) {
            subClass.getAll_super_classes().forEach(reference -> {
                ClassEntity superClass = factory.createClassEntity(reference.getTypeDeclaration());
                Inheritance inheritance = new Inheritance(subClass, superClass);
                edp_set.add(inheritance);
                createEdpRelation(inheritance, subClass, Inheritance.Role.subClass);
                createEdpRelation(inheritance, superClass, Inheritance.Role.superClass);
            });
            subClass.getAll_super_interfaces().forEach(reference -> {
                ClassEntity superClass = factory.createClassEntity(reference.getTypeDeclaration());
                Inheritance inheritance = new Inheritance(subClass, superClass);
                edp_set.add(inheritance);
                createEdpRelation(inheritance, subClass, Inheritance.Role.subClass);
                createEdpRelation(inheritance, superClass, Inheritance.Role.superClass);
            });
        }
    }

    public void parseAbstractInterface() {
        for (ExecutableEntity entity : EntityFactory.executableEntity_set) {
            if (entity instanceof MethodEntity) {
                if (entity.getModifiers().contains("abstract")) {
                    MethodEntity operation = (MethodEntity) entity;
                    ClassEntity abstractor = factory.getClassEntityByQualifiedName(entity.getDeclaration_class().getQualifiedName());
                    AbstractInterface abstractInterface = new AbstractInterface(abstractor, operation);
                    edp_set.add(abstractInterface);
                    createEdpRelation(abstractInterface, abstractor, AbstractInterface.Role.abstractor);
                    createEdpRelation(abstractInterface, operation, AbstractInterface.Role.operation);
                }
            }
        }
    }

    public void parseDelegationAndRedirection() {
        for (AbstractInvocationEntity entity : EntityFactory.abstractInvocationEntity_set) {
            if (entity instanceof InvocationEntity) {
                InvocationEntity invocationEntity = (InvocationEntity) entity;
                if (invocationEntity.getCaller_obj() != null) {
                    if (invocationEntity.getDeclaration_in() instanceof CtMethod) {
                        MethodEntity operation = factory.createMethodEntity((CtMethod<?>) invocationEntity.getDeclaration_in());
                        ClassEntity delegator = factory.createClassEntity(operation.getDeclaration_class());
                        ClassEntity delegate = factory.getClassEntityByQualifiedName(invocationEntity.getCalled_class().getQualifiedName());
                        for (CtMethod<?> method : delegate.getMethods()) {
                            if (method.getSignature().equals(invocationEntity.getCalled_executable().getSignature())) {
                                MethodEntity operation2 = factory.createMethodEntity(method);
                                if (!operation.getName().equals(operation2.getName())) {
                                    Delegation delegation = new Delegation(delegator, operation, delegate, operation2);
                                    edp_set.add(delegation);
                                    createEdpRelation(delegation, delegator, Delegation.Role.delegator);
                                    createEdpRelation(delegation, operation, Delegation.Role.operation);
                                    createEdpRelation(delegation, delegate, Delegation.Role.delegate);
                                    createEdpRelation(delegation, operation2, Delegation.Role.operation2);
                                } else {
                                    Redirection redirection = new Redirection(delegator, operation, delegate, operation2);
                                    edp_set.add(redirection);
                                    createEdpRelation(redirection, delegator, Redirection.Role.redirector);
                                    createEdpRelation(redirection, operation, Redirection.Role.operation);
                                    createEdpRelation(redirection, delegate, Redirection.Role.redirectee);
                                    createEdpRelation(redirection, operation2, Redirection.Role.operation2);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void parseConglomeration() {
        for (AbstractInvocationEntity abstractInvocationEntity : EntityFactory.abstractInvocationEntity_set) {
            if (abstractInvocationEntity instanceof InvocationEntity
                    && abstractInvocationEntity.getDeclaration_in() instanceof CtMethod
                    && !abstractInvocationEntity.getCalled_executable().isConstructor()) {
                MethodEntity operation = factory.createMethodEntity((CtMethod<?>) abstractInvocationEntity.getDeclaration_in());
                ClassEntity conglomerator = factory.createClassEntity(operation.getDeclaration_class());
                MethodEntity operation2 = factory.createMethodEntity((CtMethod<?>) abstractInvocationEntity.getCalled_executable().getExecutableDeclaration());
                Conglomeration conglomeration = new Conglomeration(conglomerator, operation, operation2);
                edp_set.add(conglomeration);
                createEdpRelation(conglomeration, conglomerator, Conglomeration.Role.conglomerator);
                createEdpRelation(conglomeration, operation, Conglomeration.Role.operation);
                createEdpRelation(conglomeration, operation2, Conglomeration.Role.operation2);
            }
        }
    }

    public void parseRecursion() {
        for (AbstractInvocationEntity abstractInvocationEntity : EntityFactory.abstractInvocationEntity_set) {
            if (abstractInvocationEntity instanceof InvocationEntity
                    && abstractInvocationEntity.getDeclaration_in() instanceof CtMethod
                    && abstractInvocationEntity.getCalled_executable().equals(((CtMethod<?>) abstractInvocationEntity.getDeclaration_in()).getReference())) {
                MethodEntity operation = factory.createMethodEntity((CtMethod<?>) abstractInvocationEntity.getDeclaration_in());
                ClassEntity recursor = factory.createClassEntity(operation.getDeclaration_class());
                Recursion recursion = new Recursion(recursor, operation);
                edp_set.add(recursion);
                createEdpRelation(recursion, recursor, Recursion.Role.recursor);
                createEdpRelation(recursion, operation, Recursion.Role.operation);
            }
        }
    }

    public void parseRevertMethod() {
        for (ExecutableEntity executableEntity : EntityFactory.executableEntity_set) {
            if (executableEntity instanceof MethodEntity
                    && !((MethodEntity) executableEntity).getOverride_classes().isEmpty()) {
                MethodEntity operation = (MethodEntity) executableEntity;
                ClassEntity revertedBehavior = factory.createClassEntity(executableEntity.getDeclaration_class());
                if (revertedBehavior.getSuper_class() != null) {
                    ClassEntity originalBehavior = factory.getClassEntityByQualifiedName(revertedBehavior.getSuper_class().getQualifiedName());
                    for (BaseEntity outgoing : SpoonUtil.getOutgoings(operation, RelTypeEnum.HAS_INVOKE)) {
                        if (outgoing instanceof InvocationEntity) {
                            InvocationEntity invocationEntity = (InvocationEntity) outgoing;
                            if (invocationEntity.getCallerType().equals(CallerTypeEnum.SUPER)) {
                                MethodEntity operation2 = (MethodEntity) SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.CALLED);
                                RevertMethod revertMethod = new RevertMethod(originalBehavior, operation, operation2, revertedBehavior);
                                if (operation2 != null) {
                                    for (BaseEntity entity : SpoonUtil.getOutgoings(revertedBehavior, RelTypeEnum.HAS_METHOD)) {
                                        MethodEntity methodEntity = (MethodEntity) entity;
                                        if (methodEntity.getSignature().equals(operation2.getSignature())) {
                                            edp_set.add(revertMethod);
                                            createEdpRelation(revertMethod, originalBehavior, RevertMethod.Role.originalBehavior);
                                            createEdpRelation(revertMethod, operation, RevertMethod.Role.operation);
                                            createEdpRelation(revertMethod, operation2, RevertMethod.Role.operation2);
                                            createEdpRelation(revertMethod, revertedBehavior, RevertMethod.Role.revertedBehavior);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void parseExtendMethod() {
        for (ExecutableEntity executableEntity : EntityFactory.executableEntity_set) {
            if (executableEntity instanceof MethodEntity
                    && !((MethodEntity) executableEntity).getOverride_classes().isEmpty()) {
                MethodEntity operation = (MethodEntity) executableEntity;
                ClassEntity extendedBehavior = factory.createClassEntity(executableEntity.getDeclaration_class());
                if (extendedBehavior.getSuper_class() != null) {
                    ClassEntity originalBehavior = factory.getClassEntityByQualifiedName(extendedBehavior.getSuper_class().getQualifiedName());
                    Set<BaseEntity> outgoings = SpoonUtil.getOutgoings(operation, RelTypeEnum.HAS_INVOKE);
                    for (BaseEntity outgoing : outgoings) {
                        if (outgoing instanceof InvocationEntity) {
                            InvocationEntity invocationEntity = (InvocationEntity) outgoing;
                            if (invocationEntity.getCallerType().equals(CallerTypeEnum.SUPER)) {
                                MethodEntity operation2 = (MethodEntity) SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.CALLED);
                                if (operation2 != null && operation2.getSignature().equals(operation.getSignature())) {
                                    for (BaseEntity outgoing1 : outgoings) {
                                        if (!((InvocationEntity) outgoing1).getCallerType().equals(CallerTypeEnum.SUPER)) {
                                            ExtendMethod extendMethod = new ExtendMethod(originalBehavior, operation, extendedBehavior, operation2);
                                            edp_set.add(extendMethod);
                                            createEdpRelation(extendMethod, originalBehavior, ExtendMethod.Role.originalBehavior);
                                            createEdpRelation(extendMethod, operation, ExtendMethod.Role.operation);
                                            createEdpRelation(extendMethod, extendedBehavior, ExtendMethod.Role.extendedBehavior);
                                            createEdpRelation(extendMethod, operation2, ExtendMethod.Role.operation2);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void parseDelegatedConglomeration() {
        for (RelationEntity relation : EntityFactory.relationship_set) {
            BaseEntity source = relation.getSource();
            BaseEntity target = relation.getTarget();
            RelTypeEnum relType = relation.getRelation_type();
            if (source instanceof ClassEntity && target instanceof VariableEntity && relType.equals(RelTypeEnum.HAS_FIELD)) {
                ClassEntity delegator = (ClassEntity) source;
                VariableEntity delegatorTarget = (VariableEntity) target;
                if (delegator.getName().equals(delegatorTarget.getOrigin().getQualifiedName())) {
                    InvocationEntity invocationEntity = (InvocationEntity) SpoonUtil.getSingleIncoming(delegatorTarget, RelTypeEnum.OBJ);
                    if (invocationEntity != null) {
                        MethodEntity operation = (MethodEntity) SpoonUtil.getSingleIncoming(invocationEntity, RelTypeEnum.HAS_INVOKE);
                        MethodEntity operation2 = (MethodEntity) SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.CALLED);
                        if (operation != null && operation2 != null
                                && delegator.getName().equals(operation.getDeclaration_class().getQualifiedName())
                                && delegator.getName().equals(operation2.getDeclaration_class().getQualifiedName())) {
                            DelegatedConglomeration delegatedConglomeration = new DelegatedConglomeration(delegator, delegatorTarget, operation, operation2);
                            edp_set.add(delegatedConglomeration);
                            createEdpRelation(delegatedConglomeration, delegator, DelegatedConglomeration.Role.delegator);
                            createEdpRelation(delegatedConglomeration, delegatorTarget, DelegatedConglomeration.Role.delegatorTarget);
                            createEdpRelation(delegatedConglomeration, operation, DelegatedConglomeration.Role.operation);
                            createEdpRelation(delegatedConglomeration, operation2, DelegatedConglomeration.Role.operation2);
                        }
                    }
                }
            }
        }
    }

    public void parseRedirectedRecursion() {
        for (RelationEntity relation : EntityFactory.relationship_set) {
            BaseEntity source = relation.getSource();
            BaseEntity target = relation.getTarget();
            RelTypeEnum relType = relation.getRelation_type();
            if (source instanceof ClassEntity && target instanceof VariableEntity && relType.equals(RelTypeEnum.HAS_FIELD)) {
                ClassEntity recursor = (ClassEntity) source;
                VariableEntity redirectTarget = (VariableEntity) target;
                if (recursor.getName().equals(redirectTarget.getOrigin().getQualifiedName())) {
                    InvocationEntity invocationEntity = (InvocationEntity) SpoonUtil.getSingleIncoming(redirectTarget, RelTypeEnum.OBJ);
                    if (invocationEntity != null) {
                        MethodEntity operation = (MethodEntity) SpoonUtil.getSingleIncoming(invocationEntity, RelTypeEnum.HAS_INVOKE);
                        MethodEntity operation2 = (MethodEntity) SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.CALLED);
                        if (operation != null && operation.equals(operation2)
                                && recursor.getName().equals(operation.getDeclaration_class().getQualifiedName())) {
                            RedirectedRecursion redirectedRecursion = new RedirectedRecursion(recursor, redirectTarget, operation);
                            edp_set.add(redirectedRecursion);
                            createEdpRelation(redirectedRecursion, recursor, RedirectedRecursion.Role.recursor);
                            createEdpRelation(redirectedRecursion, redirectTarget, RedirectedRecursion.Role.redirectTarget);
                            createEdpRelation(redirectedRecursion, operation, RedirectedRecursion.Role.operation);
                        }
                    }
                }
            }
        }
    }

    public void parseTrustedDelegationAndTrustRedirection() {
        for (RelationEntity relation : EntityFactory.relationship_set) {
            BaseEntity source = relation.getSource();
            BaseEntity target = relation.getTarget();
            RelTypeEnum relType = relation.getRelation_type();
            if (source instanceof ClassEntity && target instanceof VariableEntity && relType.equals(RelTypeEnum.HAS_FIELD)) {
                ClassEntity delegator = (ClassEntity) source;
                CtTypeReference<?> origin_cls = ((VariableEntity) target).getOrigin();
                if (delegator.getAll_super_classes().contains(origin_cls) || delegator.getAll_super_interfaces().contains(origin_cls)) {
                    if (origin_cls.getSuperclass() == null && origin_cls.getSuperInterfaces().isEmpty()) {
                        ClassEntity familyHead = factory.getClassEntityByQualifiedName(origin_cls.getQualifiedName());
                        Set<BaseEntity> outgoings = SpoonUtil.getOutgoings(delegator, RelTypeEnum.HAS_METHOD);
                        for (BaseEntity outgoing : outgoings) {
                            MethodEntity operation = (MethodEntity) outgoing;
                            InvocationEntity invocationEntity = (InvocationEntity) SpoonUtil.getSingleOutgoing(operation, RelTypeEnum.HAS_INVOKE);
                            if (invocationEntity != null) {
                                if (target.equals(SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.OBJ))) {
                                    MethodEntity operation2 = (MethodEntity) SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.CALLED);
                                    if (operation2 != null && familyHead.equals(SpoonUtil.getSingleIncoming(operation2, RelTypeEnum.HAS_METHOD))) {
                                        if (!operation2.getSignature().equals(operation.getSignature())) {
                                            TrustedDelegation trustedDelegation = new TrustedDelegation(familyHead, delegator, (VariableEntity) target, operation, operation2);
                                            edp_set.add(trustedDelegation);
                                            createEdpRelation(trustedDelegation, familyHead, TrustedDelegation.Role.familyHead);
                                            createEdpRelation(trustedDelegation, delegator, TrustedDelegation.Role.delegator);
                                            createEdpRelation(trustedDelegation, target, TrustedDelegation.Role.target);
                                            createEdpRelation(trustedDelegation, operation, TrustedDelegation.Role.operation);
                                            createEdpRelation(trustedDelegation, operation2, TrustedDelegation.Role.operation2);
                                        } else {
                                            TrustRedirection trustRedirection = new TrustRedirection(familyHead, delegator, (VariableEntity) target, operation, operation2);
                                            edp_set.add(trustRedirection);
                                            createEdpRelation(trustRedirection, familyHead, TrustRedirection.Role.familyHead);
                                            createEdpRelation(trustRedirection, delegator, TrustRedirection.Role.redirector);
                                            createEdpRelation(trustRedirection, target, TrustRedirection.Role.target);
                                            createEdpRelation(trustRedirection, operation, TrustRedirection.Role.operation);
                                            createEdpRelation(trustRedirection, operation2, TrustRedirection.Role.operation2);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void parseDeputizedDelegationAndDeputizedRedirection() {
        for (RelationEntity relation : EntityFactory.relationship_set) {
            BaseEntity source = relation.getSource();
            BaseEntity target = relation.getTarget();
            RelTypeEnum relType = relation.getRelation_type();
            if (source instanceof ClassEntity && target instanceof VariableEntity && relType.equals(RelTypeEnum.HAS_FIELD)) {
                ClassEntity delegator = (ClassEntity) source;
                CtTypeReference<?> sibling_cls = ((VariableEntity) target).getOrigin();
                if (sibling_cls != null) {
                    ClassEntity delegateSibling = factory.getClassEntityByQualifiedName(sibling_cls.getQualifiedName());
                    if (delegateSibling != null && delegator != delegateSibling) {
                        boolean flag = true;
                        Set<CtTypeReference<?>> set1 = delegator.getAll_super_classes();
                        Set<CtTypeReference<?>> set2 = delegateSibling.getAll_super_classes();
                        for (CtTypeReference<?> reference : set1) {
                            if (reference.getQualifiedName().equals(delegateSibling.getName())) {
                                flag = false;
                            }
                        }
                        for (CtTypeReference<?> reference : set2) {
                            if (reference.getQualifiedName().equals(delegator.getName())) {
                                flag = false;
                            }
                        }
                        CtTypeReference<?> ancient = null;
                        if (flag) {
                            for (CtTypeReference<?> reference1 : set1) {
                                for (CtTypeReference<?> reference2 : set2) {
                                    if (reference1.equals(reference2)) {
                                        ancient = reference1;
                                        break;
                                    }
                                }
                            }
                        }
                        if (ancient != null) {
                            ClassEntity familyHead = factory.createClassEntity(ancient.getTypeDeclaration());
                            Set<BaseEntity> outgoings = SpoonUtil.getOutgoings(delegator, RelTypeEnum.HAS_METHOD);
                            if (familyHead != null) {
                                for (BaseEntity outgoing : outgoings) {
                                    MethodEntity operation = (MethodEntity) outgoing;
                                    InvocationEntity invocationEntity = (InvocationEntity) SpoonUtil.getSingleOutgoing(operation, RelTypeEnum.HAS_INVOKE);
                                    if (invocationEntity != null) {
                                        if (target.equals(SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.OBJ))) {
                                            MethodEntity operation2 = (MethodEntity) SpoonUtil.getSingleOutgoing(invocationEntity, RelTypeEnum.CALLED);
                                            if (operation2 != null) {
                                                flag = false;
                                                for (CtType<?> override_class : operation2.getOverride_classes()) {
                                                    if (override_class.getQualifiedName().equals(familyHead.getName())) {
                                                        flag = true;
                                                    }
                                                }
                                                if (flag) {
                                                    if (!operation2.getSignature().equals(operation.getSignature())) {
                                                        DeputizedDelegation deputizedDelegation = new DeputizedDelegation(familyHead, delegator, delegateSibling, (VariableEntity) target, operation, operation2);
                                                        edp_set.add(deputizedDelegation);
                                                        createEdpRelation(deputizedDelegation, familyHead, DeputizedDelegation.Role.familyHead);
                                                        createEdpRelation(deputizedDelegation, delegator, DeputizedDelegation.Role.delegator);
                                                        createEdpRelation(deputizedDelegation, delegateSibling, DeputizedDelegation.Role.delegateSibling);
                                                        createEdpRelation(deputizedDelegation, target, DeputizedDelegation.Role.target);
                                                        createEdpRelation(deputizedDelegation, operation, DeputizedDelegation.Role.operation);
                                                        createEdpRelation(deputizedDelegation, operation2, DeputizedDelegation.Role.operation2);
                                                    } else {
                                                        DeputizedRedirection deputizedRedirection = new DeputizedRedirection(familyHead, delegator, delegateSibling, (VariableEntity) target, operation, operation2);
                                                        edp_set.add(deputizedRedirection);
                                                        createEdpRelation(deputizedRedirection, familyHead, DeputizedRedirection.Role.familyHead);
                                                        createEdpRelation(deputizedRedirection, delegator, DeputizedRedirection.Role.redirector);
                                                        createEdpRelation(deputizedRedirection, target, DeputizedRedirection.Role.target);
                                                        createEdpRelation(deputizedRedirection, operation, DeputizedRedirection.Role.operation);
                                                        createEdpRelation(deputizedRedirection, operation2, DeputizedRedirection.Role.operation2);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void parse() {
        parseCreateObject();
        parseRetrieve();
        parseInheritance();
        parseAbstractInterface();
        parseDelegationAndRedirection();
        parseConglomeration();
        parseRecursion();
        parseRevertMethod();
        parseExtendMethod();
        parseExtendMethod();
        parseDelegatedConglomeration();
        parseRedirectedRecursion();
        parseTrustedDelegationAndTrustRedirection();
        parseDeputizedDelegationAndDeputizedRedirection();
    }

    public Set<RelationEntity> getEdpRelationEntities() {
        return EntityFactory.edp_relationship_set;
    }
}
